﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using GNF.Geo;
using System.Diagnostics;

namespace GNF.GNFService.GNFClient
{
    /// <summary>
    /// a GNF Service Client, can subsribe to the server and send an event whenever the GPS Data has changed
    /// </summary>
    public class GNFServiceClient : IGPSDataCallback, IDisposable
    {

        #region private memebrs
        /// <summary>
        /// holds the channel to the server
        /// </summary>
        protected IGPSDataContract pipeProxy = null;

        /// <summary>
        /// the connection to the server
        /// </summary>
        protected DuplexChannelFactory<IGPSDataContract> pipeFactory = null;
        
        /// <summary>
        /// Contain the latest GPSData
        /// </summary>
        protected GPSData _Data = null;

        #endregion

        #region public properties

        /// <summary>
        /// holds the latest GPS Data
        /// </summary>
        public GPSData GPSDataRef
        {
            get
            {
                return _Data;
            }
        }

        #endregion

        #region contruction and init

        /// <summary>
        /// a contructor that get a refernece to a GPSData that he change when ever a new data arrive
        /// </summary>
        /// <param name="RefData"></param>
        public GNFServiceClient(GPSData RefData)
        {
            _Data = RefData;
        }

        /// <summary>
        /// defualt constructor
        /// </summary>
        public GNFServiceClient() 
        { 
            _Data = new GPSData(); 
        }

        #endregion

        #region communication related methods

        /// <summary>
        /// open a channel to the server and Subscribe to it
        /// </summary>
        /// <returns>true if connection succeeded,false other wise</returns>
        public bool Connect()
        {  
            try
            {
                if (IsCommunicationOpen) return true;
                else
                {
                    //note the "DuplexChannelFactory".  This is necessary for Callbacks.
                    // A regular "ChannelFactory" won't work with callbacks.
                    pipeFactory =
                          new DuplexChannelFactory<IGPSDataContract>(
                              new InstanceContext(this),
                              new NetTcpBinding(),
                              new EndpointAddress("net.tcp://localhost:8000/ISubscribe"));

                    //Open the channel to the server
                    pipeProxy = pipeFactory.CreateChannel();
                }
                //Now tell the server who is connecting
                if (pipeProxy.Subscribe())
                {
                    WriteToDebug("Connected to server");
                    return true;
                }
                //could not connect to the Server
                WriteToDebug("Could not connect to server");
                return false;
            }
            catch (Exception e)
            {
                WriteToDebug(e.Message);
                //Console.WriteLine(e.Message);
                return false;
            }
        }

        /// <summary>
        /// try to close the channel between the client to the server using the unsubscribe method
        /// </summary>
        public void Close()
        {
            try
            {
                if (!pipeProxy.Unsubscribe())
                {
                    pipeProxy.Unsubscribe();
                }
                pipeProxy = null;
            }
            catch (Exception)
            {
            }

        }

        /// <summary>
        /// true for a opened communication channel
        /// </summary>
        public bool IsCommunicationOpen
        {
            get
            {
                return pipeFactory == null ? false : ((ICommunicationObject)pipeFactory).State == CommunicationState.Opened;   
            }
        }

        #endregion

        #region IDisposable Members
        /// <summary>
        /// close the connection to the server if the client is closed
        /// </summary>
        public void Dispose()
        {
            Close();
        }

        #endregion

        #region IGPSDataCallback Members

        /// <summary>
        /// the callback to the server that fire an event of GPSDataChanged to all listeners
        /// </summary>
        /// <param name="data">the GPS data that was sent from the server</param>
        /// <param name="timestamp">the time of the GPS Data was taken on</param>
        public void OnDataChanged(GPSData data, DateTime timestamp)
        {
            //will be executed only in debug mode
            WriteToDebug("Got new Data (Date:" + timestamp.ToString("hh:mm:ss") + "): " + data);

            //update the local data
            if (_Data == null)
            {
                _Data = new GPSData(data);
            }
            else
            {
                _Data.ChangeTo(data);
            }
            
            //fire the event of the change
            if (GPSDataChanged != null)
            {
                GPSDataChanged(data, timestamp);
            }
        }

        #endregion

        #region events

        /// <summary>
        /// indicate that the data of the GPS has Changed
        /// </summary>
        public event TimedOnGPSDataChangedEventHandler GPSDataChanged;

        #endregion

        #region debug methods

        /// <summary>
        /// write a messgae to the console in debug mode
        /// </summary>
        /// <param name="s">the message to write to the consloe</param>
        [Conditional("DEBUG")]
        private void WriteToDebug(string s)
        {
            Console.WriteLine("Client ID:" + this.GetHashCode()+" ,"+s);
        }

        #endregion
    }
}
